import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;

public class printAst
{
    public static void println(OutputStreamWriter writer, int layer, String src) throws IOException
    {
        for(int i=0; i<layer; i++)
            writer.write("  ");
        writer.write(src + "\n");
    }
    public static void printDecl(Decl decl, int layer, OutputStreamWriter writer) throws IOException
    {
        if(decl == null)
        {
            println(writer, layer, "null");
            return;
        }
        switch(decl.getClass().getName())
        {
            case "FuncDecl": printFuncDecl((FuncDecl) decl, layer, writer);break;
            case "ValueDecl": printValueDecl((ValueDecl) decl, layer, writer);break;
            case "TypeDecl": printTypeDecl((TypeDecl) decl, layer, writer);break;
            default: System.out.println("unimplement " + decl.getClass().getName() + ".Location: printDecl");
        }
    }

    public static void printFuncDecl(FuncDecl funcDecl, int layer, OutputStreamWriter writer)
    {
        try
        {
            println(writer, 0,"Function Declare");
            println(writer, 1,"Name");
            printExpr(funcDecl.name,2,writer);
            println(writer, 1, "Receiver");
            for(Field receiver : funcDecl.receiver)
                printExpr(receiver, 2, writer);
            println(writer, 1,"Params");
            for(Field param : funcDecl.type.params)
                printExpr(param, 2, writer);
            println(writer,1,"Results");
            for(Field result : funcDecl.type.results)
                printExpr(result, 2, writer);
            println(writer,1,"Statements");
            printStatement(funcDecl.body, 2,writer);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public static void printValueDecl(ValueDecl valueDecl, int layer, OutputStreamWriter writer) throws IOException
    {
        println(writer, 0, "Value Declare");
        println(writer, 1, "names");
        for(Id name : valueDecl.names)
            printExpr(name, 2,writer);
        println(writer,1,"type");
        printExpr(valueDecl.type, 2, writer);
        println(writer,1,"values");
        for(Expr value : valueDecl.value)
            printExpr(value,2,writer);
    }

    public static void printTypeDecl(TypeDecl typeDecl, int layer, OutputStreamWriter writer) throws IOException
    {
        if(typeDecl == null)
        {
            println(writer, layer, "null");
            return;
        }
        println(writer, layer, "TypeDecl");
        println(writer, layer+1, "id");
        printExpr(typeDecl.id, layer+2, writer);
        println(writer, layer+1, "type");
        printExpr(typeDecl.type, layer+2, writer);
        println(writer, layer+1, "isAssign: " + typeDecl.isAssign);
    }

    public static void printExpr(Expr expr, int layer, OutputStreamWriter writer) throws IOException
    {
        if(expr == null)
        {
            println(writer, layer, "null");
            return;
        }
        println(writer, layer, expr.getClass().getName());
        switch(expr.getClass().getName())
        {
            case "Id": println(writer, layer+1, ((Id)expr).name);break;
            case "SelectorExpr":
                SelectorExpr selectorExpr = (SelectorExpr)expr;
                println(writer, layer+1,"Expr");
                printExpr(((SelectorExpr)expr).expr, layer+2, writer);
                println(writer, layer+1, "Sel");
                printExpr(((SelectorExpr)expr).sel, layer+2, writer);
                break;
            case "Expression":
                Expression expression = (Expression) expr;
                println(writer, layer+1, "A");
                printExpr(expression.A, layer+2, writer);
                println(writer, layer+1, "B");
                printExpr(expression.B, layer+2, writer);
                println(writer,layer+1, "OP");
                println(writer, layer+2,expression.op.toString());
                break;
            case "UnaryExpr":
                UnaryExpr unaryExpr = (UnaryExpr) expr;
                println(writer, layer+1, "x");
                printExpr(unaryExpr.x, layer+2, writer);
                println(writer, layer+1, "OP");
                println(writer,layer+2, unaryExpr.op.toString());
                break;
            case "BasicLit":
                BasicLit basicLit = (BasicLit) expr;
                println(writer, layer+1, "value");
                println(writer, layer+2, basicLit.value);
                println(writer, layer+2, "kind");
                println(writer,layer+2, basicLit.kind.toString());
                break;
            case "KeyValueExpr":
                KeyValueExpr keyValueExpr = (KeyValueExpr) expr;
                println(writer, layer+1, "key");
                printExpr(keyValueExpr.key, layer+2, writer);
                println(writer, layer+1, "value");
                printExpr(keyValueExpr.value, layer+2, writer);
                break;
            case "Ellipsis":
                Ellipsis ellipsis = (Ellipsis) expr;
                println(writer, layer+1, "x");
                printExpr(ellipsis.x, layer+2, writer);
                break;
            case "CompositeExpr":
                CompositeExpr compositeExpr = (CompositeExpr) expr;
                println(writer, layer+1, "type");
                printExpr(compositeExpr.type, layer+2, writer);
                println(writer, layer+1, "list");
                for(Expr expr1 : compositeExpr.list)
                    printExpr(expr1, layer+2,  writer);
                break;
            case "CompositeLit":
                CompositeLit compositeLit = (CompositeLit) expr;
                println(writer, layer+1, "type");
                printExpr(compositeLit.type, layer+2, writer);
                println(writer, layer+1, "value");
                for(Expr expr1 : compositeLit.value)
                    printExpr(expr1, layer+2, writer);
                break;
            case "Field":
                Field field = (Field) expr;
                println(writer, layer+1, "type");
                printExpr(field.type, layer+2, writer);
                println(writer, layer+1, "id");
                printExpr(field.id, layer+2, writer);
                break;
            case "StarType":
                StarType starType = (StarType) expr;
                println(writer, layer+1, "type");
                printExpr(starType.type, layer+2, writer);
                break;
            case "StructExpr":
                StructExpr structExpr = (StructExpr) expr;
                println(writer, layer+1, "fields");
                for(Field field1 : structExpr.fields)
                    printExpr(field1, layer+2, writer);
                println(writer, layer+1, "tags");
                for(BasicLit basicLit1 : structExpr.tags)
                    printExpr(basicLit1, layer+2, writer);
                break;
            case "ArrayType":
                ArrayType arrayType = (ArrayType) expr;
                println(writer, layer+1, "len");
                printExpr(arrayType.len, layer+2, writer);
                println(writer, layer+1, "type");
                printExpr(arrayType.type, layer+2, writer);
                break;
            case "FuncType":
                FuncType funcType = (FuncType) expr;
                println(writer, layer+1, "params");
                for(Field field1 : funcType.params)
                    printExpr(field1, layer+2, writer);
                println(writer, layer+1, "results");
                for(Field field1 : funcType.results)
                    printExpr(field1, layer+2, writer);
                break;
            case "CallExpr":
                CallExpr callExpr = (CallExpr) expr;
                println(writer, layer+1, "func");
                printExpr(callExpr.func, layer+2, writer);
                println(writer, layer+1, "args");
                for(Expr expr1 : callExpr.args)
                    printExpr(expr1, layer+2, writer);
                println(writer, layer+1, "hasDot: " + callExpr.hasDot);
                break;
            case "InterfaceType":
                InterfaceType interfaceType = (InterfaceType) expr;
                println(writer, layer+1, "methods");
                for(Field field1 : interfaceType.methods)
                    printExpr(field1,layer+2, writer);
                break;
            case "SliceExpr":
                SliceExpr sliceExpr = (SliceExpr) expr;
                println(writer, layer+1, "x");
                printExpr(sliceExpr.x,layer+2, writer);
                println(writer, layer+1, "index");
                for(int i=0; i<3; i++)
                    printExpr(sliceExpr.index[i], layer+2, writer);
                println(writer, layer+1, "slice3: " + sliceExpr.slice3);
                break;
            case "MapType":
                MapType mapType = (MapType) expr;
                println(writer, layer+1, "keyType");
                printExpr(mapType.keyType, layer+2, writer);
                println(writer, layer+2, "valueType");
                printExpr(mapType.valueType, layer+2, writer);
                break;
            case "ChannelType":
                ChannelType channelType = (ChannelType) expr;
                println(writer, layer+1, "dir");
                println(writer, layer+2, channelType.dir.toString());
                println(writer, layer+1, "type");
                printExpr(channelType.type, layer+2, writer);
                break;
            case "FunctionLit":
                FunctionLit functionLit = (FunctionLit) expr;
                println(writer, layer+1, "signature");
                printExpr(functionLit.signature, layer+2, writer);
                println(writer, layer+1, "block");
                printStatement(functionLit.block, layer+2, writer);
                break;
            case "Signature":
                Signature signature = (Signature) expr;
                println(writer, layer+1, "param");
                for(Field field1 : signature.param)
                    printExpr(field1, layer+2, writer);
                println(writer, layer+1, "results");
                for(Field field1 : signature.results)
                    printExpr(field1, layer+2, writer);
                break;
            case "IndexExpr":
                IndexExpr indexExpr = (IndexExpr) expr;
                println(writer, layer+1, "x");
                printExpr(indexExpr.x,layer+2, writer);
                println(writer, layer+1, "index");
                printExpr(indexExpr.index, layer+2, writer);
                break;
            case "TypeAssertExpr":
                TypeAssertExpr typeAssertExpr = (TypeAssertExpr) expr;
                println(writer, layer+1, "x");
                printExpr(typeAssertExpr.x, layer+2, writer);
                println(writer, layer+1, "type");
                printExpr(typeAssertExpr.type, layer+2, writer);
                break;
            default: System.out.println("未实现 " + expr.getClass().getName());
        }
    }

    public static void printStatement(Statement statement, int layer, OutputStreamWriter writer) throws IOException
    {
        if(statement == null)
        {
            println(writer, layer, "null");
            return;
        }
        println(writer, layer,statement.getClass().getName());
        switch(statement.getClass().getName())
        {
            case "LabelStatement":
                LabelStatement labelStatement = (LabelStatement) statement;
                println(writer, layer+1, "label");
                printExpr(labelStatement.label,  layer+2, writer);
                println(writer, layer+1, "statement");
                printStatement(labelStatement.statement, layer+2, writer);
                break;
            case "SendStatement":
                SendStatement sendStatement = (SendStatement) statement;
                println(writer, layer+1, "channel");
                printExpr(sendStatement.channel,  layer+2, writer);
                println(writer, layer+1, "value");
                printExpr(sendStatement.value, layer+2, writer);
                break;
            case "IncDecStatement":
                IncDecStatement incDecStatement = (IncDecStatement) statement;
                println(writer, layer+1, "x");
                printExpr(incDecStatement.x, layer+2, writer);
                println(writer, layer+1, "op");
                println(writer, layer+2, incDecStatement.op.toString());
                break;
            case "AssignStatement":
                AssignStatement assignStatement = (AssignStatement) statement;
                println(writer, layer+1, "left");
                for(Expr expr : assignStatement.left)
                    printExpr(expr, layer+2, writer);
                println(writer, layer+1, "right");
                for(Expr expr : assignStatement.right)
                    printExpr(expr, layer+2, writer);
                println(writer, layer+1, "op");
                if(assignStatement.op != null)
                    println(writer, layer+2, assignStatement.op.toString());
                println(writer, layer+1, "isRange: " + assignStatement.isRange);
                break;
            case "ExpressionStatement":
                ExpressionStatement expressionStatement = (ExpressionStatement) statement;
                println(writer, layer+1, "expr");
                printExpr(expressionStatement.expr, layer+2, writer);
                break;
            case "EmptyStatement":break;
            case "CallStatement":
                CallStatement callStatement = (CallStatement) statement;
                println(writer, layer+1, "dest");
                printExpr(callStatement.dest, layer+2, writer);
                println(writer, layer+1, "type");
                println(writer, layer+2, callStatement.type.toString());
                break;
            case "ReturnStatement":
                ReturnStatement returnStatement = (ReturnStatement) statement;
                println(writer, layer+1, "results");
                for(Expr expr : returnStatement.results)
                    printExpr(expr, layer+2, writer);
                break;
            case "JumpStatement":
                JumpStatement jumpStatement = (JumpStatement) statement;
                println(writer, layer+1, "label");
                printExpr(jumpStatement.label, layer+2, writer);
                println(writer, layer+1, "type");
                println(writer, layer+2, jumpStatement.type.toString());
                break;
            case "BlockStatement":
                BlockStatement blockStatement = (BlockStatement) statement;
                println(writer, layer+1, "list");
                for(Statement statement1 : blockStatement.list)
                    printStatement(statement1, layer+2, writer);
                break;
            case "IFStatement":
                IFStatement ifStatement = (IFStatement) statement;
                println(writer, layer+1, "condition");
                printExpr(ifStatement.condition, layer+2, writer);
                println(writer, layer+1, "init");
                printStatement(ifStatement.init, layer+2, writer);
                println(writer, layer+1, "block");
                printStatement(ifStatement.block, layer+2, writer);
                println(writer, layer+1, "elsePart");
                printStatement(ifStatement.elsePart, layer+2, writer);
                break;
            case "TypeSwitchGuard":
                TypeSwitchGuard typeSwitchGuard = (TypeSwitchGuard) statement;
                println(writer, layer+1, "name");
                printExpr(typeSwitchGuard.name, layer+2, writer);
                println(writer, layer+1, "expr");
                printExpr(typeSwitchGuard.expr, layer+2, writer);
                break;
            case "CaseStatement":
                CaseStatement caseStatement = (CaseStatement) statement;
                println(writer, layer+1, "cases");
                for(Expr expr : caseStatement.cases)
                    printExpr(expr, layer+2, writer);
                println(writer, layer+1, "body");
                for(Statement statement1 : caseStatement.body)
                    printStatement(statement1, layer+2, writer);
                break;
            case "TypeSwitchStatement":
                TypeSwitchStatement typeSwitchStatement = (TypeSwitchStatement) statement;
                println(writer, layer+1, "init");
                printStatement(typeSwitchStatement.init, layer+2, writer);
                println(writer, layer+1, "tag");
                printStatement(typeSwitchStatement.tag, layer+2, writer);
                println(writer, layer+1, "body");
                for(Statement statement1 : typeSwitchStatement.body)
                    printStatement(statement1, layer+2, writer);
                break;
            case "CommonClause":
                CommonClause commonClause = (CommonClause) statement;
                println(writer, layer+1, "common");
                printStatement(commonClause.common, layer+2, writer);
                println(writer, layer+1, "body");
                for(Statement statement1 : commonClause.body)
                    printStatement(statement1, layer+2, writer);
                break;
            case "SelectStatement":
                SelectStatement selectStatement = (SelectStatement) statement;
                println(writer, layer+1, "body");
                for(Statement statement1 : selectStatement.body)
                    printStatement(statement1, layer+2, writer);
                break;
            case "RangeClause":
                RangeClause rangeClause = (RangeClause) statement;
                println(writer, layer+1, "left");
                for(Expr expr : rangeClause.left)
                    printExpr(expr, layer+2, writer);
                println(writer, layer+1, "define: " + rangeClause.define);
                println(writer, layer+1, "expr");
                printExpr(rangeClause.expr, layer+2, writer);
                break;
            case "ForStatement":
                ForStatement forStatement = (ForStatement) statement;
                println(writer, layer+1, "init");
                printStatement(forStatement.init, layer+2, writer);
                println(writer, layer+1, "post");
                printStatement(forStatement.post, layer+2, writer);
                println(writer, layer+1, "cond");
                printExpr(forStatement.cond, layer+2, writer);
                println(writer, layer+1, "body");
                printStatement(forStatement.body, layer+2, writer);
                break;
            case "DeclStatement":
                DeclStatement declStatement = (DeclStatement) statement;
                println(writer, layer+1, "decls");
                for(Decl decl : declStatement.decls)
                    printDecl(decl,layer+2, writer);
                break;
            default: System.out.println("未实现" + statement.getClass().getName());
        }
    }
}
